// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_VIEWS_FOCUS_FOCUS_SEARCH_H_
#define UI_VIEWS_FOCUS_FOCUS_SEARCH_H_

#include "base/macros.h"
#include "ui/views/view.h"

namespace views {

class FocusTraversable;

// FocusSearch is an object that implements the algorithm to find the
// next view to focus.
class VIEWS_EXPORT FocusSearch {
public:
    // The direction in which the focus traversal is going.
    // TODO (jcampan): add support for lateral (left, right) focus traversal. The
    // goal is to switch to focusable views on the same level when using the arrow
    // keys (ala Windows: in a dialog box, arrow keys typically move between the
    // dialog OK, Cancel buttons).
    enum Direction {
        UP = 0,
        DOWN
    };

    // Constructor.
    // - |root| is the root of the view hierarchy to traverse. Focus will be
    //   trapped inside.
    // - |cycle| should be true if you want FindNextFocusableView to cycle back
    //           to the first view within this root when the traversal reaches
    //           the end. If this is true, then if you pass a valid starting
    //           view to FindNextFocusableView you will always get a valid view
    //           out, even if it's the same view.
    // - |accessibility_mode| should be true if full keyboard accessibility is
    //   needed and you want to check IsAccessibilityFocusable(), rather than
    //   IsFocusable().
    FocusSearch(View* root, bool cycle, bool accessibility_mode);
    virtual ~FocusSearch() { }

    // Finds the next view that should be focused and returns it. If a
    // FocusTraversable is found while searching for the focusable view,
    // returns NULL and sets |focus_traversable| to the FocusTraversable
    // and |focus_traversable_view| to the view associated with the
    // FocusTraversable.
    //
    // Return NULL if the end of the focus loop is reached, unless this object
    // was initialized with |cycle|=true, in which case it goes back to the
    // beginning when it reaches the end of the traversal.
    // - |starting_view| is the view that should be used as the starting point
    //   when looking for the previous/next view. It may be NULL (in which case
    //   the first/last view should be used depending if normal/reverse).
    // - |reverse| whether we should find the next (reverse is false) or the
    //   previous (reverse is true) view.
    // - |direction| specifies whether we are traversing down (meaning we should
    //   look into child views) or traversing up (don't look at child views).
    // - |check_starting_view| is true if starting_view may obtain the next focus.
    // - |focus_traversable| is set to the focus traversable that should be
    //   traversed if one is found (in which case the call returns NULL).
    // - |focus_traversable_view| is set to the view associated with the
    //   FocusTraversable set in the previous parameter (it is used as the
    //   starting view when looking for the next focusable view).
    virtual View* FindNextFocusableView(View* starting_view,
        bool reverse,
        Direction direction,
        bool check_starting_view,
        FocusTraversable** focus_traversable,
        View** focus_traversable_view);

protected:
    // Get the parent, but stay within the root. Returns NULL if asked for
    // the parent of |root_|. Subclasses can override this if they need custom
    // focus search behavior.
    virtual View* GetParent(View* v);

    // Returns true if |v| is contained within the hierarchy rooted at |root|.
    // Subclasses can override this if they need custom focus search behavior.
    virtual bool Contains(View* root, const View* v);

    View* root() const { return root_; }

private:
    // Convenience method that returns true if a view is focusable and does not
    // belong to the specified group.
    bool IsViewFocusableCandidate(View* v, int skip_group_id);

    // Convenience method; returns true if a view is not NULL and is focusable
    // (checking IsAccessibilityFocusable() if |accessibility_mode_| is true).
    bool IsFocusable(View* v);

    // Returns the view selected for the group of the selected view. If the view
    // does not belong to a group or if no view is selected in the group, the
    // specified view is returned.
    View* FindSelectedViewForGroup(View* view);

    // Returns the next focusable view or view containing a FocusTraversable
    // (NULL if none was found), starting at the starting_view.
    // |check_starting_view|, |can_go_up| and |can_go_down| controls the
    // traversal of the views hierarchy. |skip_group_id| specifies a group_id,
    // -1 means no group. All views from a group are traversed in one pass.
    View* FindNextFocusableViewImpl(View* starting_view,
        bool check_starting_view,
        bool can_go_up,
        bool can_go_down,
        int skip_group_id,
        FocusTraversable** focus_traversable,
        View** focus_traversable_view);

    // Same as FindNextFocusableViewImpl but returns the previous focusable view.
    View* FindPreviousFocusableViewImpl(View* starting_view,
        bool check_starting_view,
        bool can_go_up,
        bool can_go_down,
        int skip_group_id,
        FocusTraversable** focus_traversable,
        View** focus_traversable_view);

    View* root_;
    bool cycle_;
    bool accessibility_mode_;

    DISALLOW_COPY_AND_ASSIGN(FocusSearch);
};

} // namespace views

#endif // UI_VIEWS_FOCUS_FOCUS_SEARCH_H_
